﻿using href.Utils;
using Microsoft.Practices.Unity;
using ModernUILogViewer.Business;
using ModernUILogViewer.Model;
using MvvmFoundation.Wpf;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reactive.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;

namespace ModernUILogViewer.ViewModel
{
    #region Fields

    public enum PROCESS_RESULT { SUCCESS, CANCELLED, DATA_PARSE_ERROR, LEVEL_PARSE_ERROR, UNAUTHORIZED_ACCESS_ERROR, GENERIC_ERROR }

    #endregion

    public class HomeViewModel : BaseViewModel
    {
        #region Constructor

        public HomeViewModel(MainViewModel mainVM)
        {
            this.mainVM = mainVM;

            this.LogEntries = new List<LogEntryViewModel>();

            listLevel = new List<LevelViewModel>();

            Enum.GetValues(typeof(LOG_LEVEL)).Cast<LOG_LEVEL>().All
                (n =>
                {
                    listLevel.Add(new LevelViewModel { Level = n, IsSelected = true });
                    return true;
                }
                );

            RaisePropertyChanged(() => ListLevel);

            Status = "Ready";

            prop = new PropertyObserver<HomeViewModel>(this);
            prop.RegisterHandler(n => n.StartDate, n => CheckDateChange(n.StartDate, startDateList, value => n.StartDate = value));
            prop.RegisterHandler(n => n.EndDate, n => CheckDateChange(n.EndDate, endDateList, value => n.EndDate = value));

            AsyncFilterEntries();
        }

        #endregion

        #region Events

        public event EventHandler EventFileTooLarge;
        public event EventHandler EventLinePatternException;
        public event EventHandler EventDatePatternException;
        public event EventHandler EventLevelPatternException;
        public event EventHandler EventDateFormatException;
        public event EventHandler EventLevelParseException;
        public event EventHandler EventGenericException;
        public event EventHandler HomeViewRequestedEvent;
        public event EventHandler UnauthorizedException;

        #endregion

        #region Fields

        private MainViewModel mainVM;
        private List<DateTime> dateList;
        private CancellationTokenSource tokenSource = new CancellationTokenSource();
        private ManualResetEvent mre = new ManualResetEvent(false);
        private const int FILTER_EXECUTING_DELAY = 500;

        private readonly PropertyObserver<HomeViewModel> prop;

        /// <summary>
        /// this bool variable ensures the main working thread 
        /// will loop once again if UI updates while executing      
        /// in order to get synchronized with UI values
        /// </summary>
        //private bool isRefreshRequested;

        private const long MAX_FILE_LENGTH = 1024 * 1024 * 150; // Byte * Kilobyte * Megabyte

        #endregion

        #region Properties

        public IList<LogEntryViewModel> LogEntries
        {
            get;
            private set;
        }

        public OptionsViewModel OptionsVM
        {
            get { return mainVM.OptionsVM; }
        }

        public int EntriesCount
        {
            get { return LogEntries.Count(); }
        }

        private IList<FileViewModel> listFiles;

        public IList<FileViewModel> ListFiles
        {
            get { return listFiles; }
            set
            {
                listFiles = value;
                RaisePropertyChanged(() => ListFiles);
            }
        }


        private string textFilter;

        public string TextFilter
        {
            get { return textFilter; }
            set
            {
                textFilter = value;
                RaisePropertyChanged(() => TextFilter);
            }
        }

        private bool matchWholeWord;

        public bool MatchWholeWord
        {
            get { return matchWholeWord; }
            set
            {
                matchWholeWord = value;
                RaisePropertyChanged(() => MatchWholeWord);
            }
        }

        private bool matchIgnoreCase;

        public bool MatchIgnoreCase
        {
            get { return matchIgnoreCase; }
            set
            {
                matchIgnoreCase = value;
                RaisePropertyChanged(() => MatchIgnoreCase);
            }
        }


        private string status;

        public string Status
        {
            get { return status; }
            set
            {
                status = value;
                RaisePropertyChanged(() => Status);
            }
        }

        private string currentProcessedFilename;

        public string CurrentProcessedFilename
        {
            get { return currentProcessedFilename; }
            set
            {
                currentProcessedFilename = value;
                RaisePropertyChanged(() => CurrentProcessedFilename);
            }
        }


        private bool isProcessing;

        public bool IsProcessing
        {
            get { return isProcessing; }
            set
            {
                isProcessing = value;
                RaisePropertyChanged(() => IsProcessing);
            }
        }

        private long bytesRead;

        public long BytesRead
        {
            get { return bytesRead; }
            private set
            {
                bytesRead = value;
                RaisePropertyChanged(() => BytesRead);
            }
        }

        private long fileSize;

        public long FileSize
        {
            get { return fileSize; }
            private set
            {
                fileSize = value;
                RaisePropertyChanged(() => FileSize);
            }
        }


        private LogEntryViewModel selectedLogEntry;

        public LogEntryViewModel SelectedLogEntry
        {
            get { return selectedLogEntry; }
            set
            {
                selectedLogEntry = value;
                RaisePropertyChanged(() => SelectedLogEntry);
            }
        }


        private IList<LevelViewModel> listLevel;

        public IList<LevelViewModel> ListLevel
        {
            get { return listLevel; }
            set
            {
                listLevel = value;
                RaisePropertyChanged(() => ListLevel);
            }
        }

        private DateTime startDate;

        public DateTime StartDate
        {
            get { return startDate; }
            set
            {
                startDate = value;
                RaisePropertyChanged(() => StartDate);
            }
        }

        private DateTime endDate;

        public DateTime EndDate
        {
            get { return endDate; }
            set
            {
                endDate = value;
                RaisePropertyChanged(() => EndDate);
            }
        }


        private List<LogEntryViewModel> filteredLogEntries = new List<LogEntryViewModel>();
        public List<LogEntryViewModel> FilteredLogEntries
        {
            get
            {
                return filteredLogEntries;
            }
            private set
            {
                filteredLogEntries = value;
                RaisePropertyChanged(() => FilteredLogEntries);
            }

        }

        private List<DateTime> startDateList;

        public List<DateTime> StartDateList
        {
            get { return startDateList; }
            set
            {
                startDateList = value;
                RaisePropertyChanged(() => StartDateList);
            }
        }

        private List<DateTime> endDateList;

        public List<DateTime> EndDateList
        {
            get { return endDateList; }
            set
            {
                endDateList = value;
                RaisePropertyChanged(() => EndDateList);
            }
        }

        #endregion

        #region Methods

        /*
         * This method is a trick to ensure the input date is within the list 
         * despite playing with the 00:00 datetimeupdown.
         * Action<DateTime> setOutput is another trick to set a property inside a function
         * check http://stackoverflow.com/questions/1402803/passing-properties-by-reference-in-c-sharp
         * 
         * */
        void CheckDateChange(DateTime input, List<DateTime> list, Action<DateTime> setOutput)
        {            
            if (input.Date != DateTime.MinValue && list.Select(o => o.Date).Contains(input.Date) == false)
            {
                input = list.OrderBy(t => Math.Abs((t - input).Ticks)).First();
                setOutput(input);
            }
        }

        void ReloadFileExecute()
        {
            if (listFiles != null)
            {
                AsyncProcessFiles(listFiles.Select(o => o.Filename).ToArray<string>());
            }
        }

        private void StartProcessFiles(string[] files)
        {
            listFiles = new List<FileViewModel>();
            files.All<string>(obj =>
            {
                listFiles.Add(new FileViewModel { Filename = obj, IsSelected = true });
                return true;
            }
        );

            RaisePropertyChanged(() => ListFiles);

            long totalFilesize = files.Sum(filelen => { return new FileInfo(filelen).Length; });

            if (totalFilesize < MAX_FILE_LENGTH)
            {
                AsyncProcessFiles(files);
            }
            else
                RaiseEventInvoker(EventFileTooLarge);
        }

        void OpenExecute()
        {
            if (CheckRegexValidation())
            {
                IUnityContainer container = UnityContainerProvider.Instance;
                IDialogService service = container.Resolve<IDialogService>(DialogServiceNS.DLG_OPEN_MULTIPLE_LOG_FILES);

                string[] files = service.OpenFile(true);

                if (files.Length > 0)
                {
                    StartProcessFiles(files);
                }
            }
        }

        private void ResetEntireFilter()
        {
            ResetDateList();

            listLevel.All(x => { x.IsSelected = true; return true; });

            TextFilter = String.Empty;

            RaisePropertyChanged(() => ListLevel);
        }



        public PROCESS_RESULT ProcessFile(string filename, int fileIndex)
        {
            string patternLine = mainVM.OptionsVM.LineBeginningRegExp;
            string patternDatetime = mainVM.OptionsVM.DateRegExp;
            string patternLevel = mainVM.OptionsVM.LevelRegExp;
            string formatDatetime = mainVM.OptionsVM.DateFormat;
            int groupMatchDatetime = mainVM.OptionsVM.DateGroupMatch;
            int groupMatchLevel = mainVM.OptionsVM.LevelGroupMatch;
            bool matchDate = mainVM.OptionsVM.IncludeDateMatch;
            bool matchLevel = mainVM.OptionsVM.IncludeLevelMatch;
            CultureInfo cultureInfo = mainVM.OptionsVM.SelectedCultureInfo;

            string line;

            List<DateTime> listDateTime = new List<DateTime>();

            DateTime lastDt = new DateTime(0);

            StringBuilder sb = new StringBuilder();

            LogEntry lastLogEntry = null;

            bytesRead = 0;

            int linecounter = 0;

            System.IO.StreamReader sr = null;

            PROCESS_RESULT result = PROCESS_RESULT.SUCCESS;

            try
            {
                sr = EncodingTools.OpenTextFile(filename);

                while ((line = sr.ReadLine()) != null)
                {
                    if (tokenSource.IsCancellationRequested)
                        return PROCESS_RESULT.CANCELLED;

                    linecounter++;

                    bytesRead += sr.CurrentEncoding.GetByteCount(line);

                    Match match = Regex.Match(line, patternLine);

                    if (match.Success)
                    {
                        if (lastLogEntry != null)
                        {
                            lastLogEntry.Message = sb.ToString();
                            LogEntryViewModel logEntryVM = new LogEntryViewModel(lastLogEntry);
                            logEntryVM.FileIndex = fileIndex;
                            LogEntries.Add(logEntryVM);
                        }

                        lastLogEntry = new LogEntry { Level = LOG_LEVEL.NONE, Line = linecounter };

                        if (matchDate)
                        {
                            match = Regex.Match(line, patternDatetime);

                            if (match.Success)
                            {
                                try
                                {
                                    DateTime dt = DateTime.ParseExact(match.Groups[groupMatchDatetime].Value, formatDatetime, cultureInfo);
                                    lastLogEntry.DTime = dt;
                                }
                                catch (Exception e)
                                {
                                    result = PROCESS_RESULT.DATA_PARSE_ERROR;
                                    throw new ApplicationException(String.Empty, e);
                                }
                            }
                        }

                        if (matchLevel)
                        {
                            match = Regex.Match(line, patternLevel);

                            if (match.Success)
                            {
                                try
                                {
                                    lastLogEntry.Level = (LOG_LEVEL)Enum.Parse(typeof(LOG_LEVEL), match.Groups[groupMatchLevel].Value);
                                }
                                catch (Exception e)
                                {
                                    result = PROCESS_RESULT.LEVEL_PARSE_ERROR;
                                    throw new ApplicationException(String.Empty, e);
                                }
                            }
                        }

                        sb = new StringBuilder();

                    }

                    sb.Append(line);
                    sb.Append(Environment.NewLine);


                }

                if (lastLogEntry != null)
                {
                    lastLogEntry.Message = sb.ToString();
                    LogEntryViewModel logEntryVM = new LogEntryViewModel(lastLogEntry);
                    logEntryVM.FileIndex = fileIndex;
                    LogEntries.Add(logEntryVM);
                }
            }
            catch (ApplicationException)
            {

            }
            catch (UnauthorizedAccessException e)
            {
                result = PROCESS_RESULT.UNAUTHORIZED_ACCESS_ERROR;
            }
            catch (Exception)
            {
                result = PROCESS_RESULT.GENERIC_ERROR;
            }
            finally
            {
                if (sr != null)
                    sr.Close();
            }

            return result;
        }

        private bool CheckRegexValidation()
        {
            string patternLine = OptionsVM.LineBeginningRegExp;
            string patternDatetime = OptionsVM.DateRegExp;
            string patternLevel = OptionsVM.LevelRegExp;

            bool canOpen = true;

            Func<string, bool> func = (str) =>
            {
                return (canOpen = Utility.IsValidRegex(str));
            };

            if (func(patternLine) == false)
                RaiseEventInvoker(EventLinePatternException);
            else if (func(patternDatetime) == false)
                RaiseEventInvoker(EventDatePatternException);
            else if (func(patternLevel) == false)
                RaiseEventInvoker(EventLevelPatternException);

            return canOpen;
        }

        private void ResetDateList()
        {
            dateList = LogEntries.Select(c => c.DTime.Date).Distinct().ToList();
            StartDateList = dateList;
            EndDateList = dateList;
            // keep date filter, otherwise reset it
            if (dateList.Contains(startDate.Date) == false)
                StartDate = DateTime.MinValue.Date;
            if (dateList.Contains(endDate.Date) == false)
                EndDate = DateTime.MinValue.Date;
        }

        private void OrderListByDate()
        {
            LogEntries = LogEntries.OrderBy(o => o.DTime).ToList();
        }

        /// <summary>
        /// Call StartProcessFiles in case of new files
        /// </summary>
        /// <param name="files"></param>
        private void AsyncProcessFiles(string[] files)
        {
            tokenSource = new CancellationTokenSource();

            IDisposable subscription = Observable.Interval(TimeSpan.FromSeconds(0.3)).ObserveOnDispatcher().Subscribe(
                inp =>
                {
                    RaisePropertyChanged(() => CurrentProcessedFilename);

                    RaisePropertyChanged(() => FileSize);

                    RaisePropertyChanged(() => EntriesCount);

                    RaisePropertyChanged(() => BytesRead);
                }
            );

            // occurs on process cancellation AFTER finally
            tokenSource.Token.Register(() =>
            {
                LogEntries.Clear();

                listFiles = null;

                // don't use clear or changes will not be notified
                filteredLogEntries = new List<LogEntryViewModel>();

                ResetDateList();

                RaisePropertyChanged(() => ListFiles);

                RaisePropertyChanged(() => EntriesCount);

                RaisePropertyChanged(() => FilteredLogEntries);

                RaisePropertyChanged(() => LogEntries);
            });

            Observable.Start(() =>
            {
                // begin of the task
                LogEntries.Clear();

                IsProcessing = true;

                int idx = 0;

                PROCESS_RESULT result = PROCESS_RESULT.SUCCESS;

                foreach (string filename in files)
                {
                    fileSize = new FileInfo(filename).Length;

                    currentProcessedFilename = filename;

                    if (tokenSource.IsCancellationRequested == false)
                        result = ProcessFile(filename, idx++);

                    if (result != PROCESS_RESULT.SUCCESS)
                        break;
                }

                return result;
            }
            ).ObserveOnDispatcher().Finally(() =>
            {
                // occurs at end of the task, before cancel and after subscribe 
                IsProcessing = false;

                CurrentProcessedFilename = null;

                subscription.Dispose();
            }
            )
            .Subscribe(res =>
            {
                PROCESS_RESULT result = (PROCESS_RESULT)res;

                if (result == PROCESS_RESULT.SUCCESS)
                {
                    // occurs on completed task
                    OrderListByDate();

                    filteredLogEntries = new List<LogEntryViewModel>(LogEntries);

                    ResetDateList();

                    RaisePropertyChanged(() => EntriesCount);

                    RaisePropertyChanged(() => LogEntries);

                    ReleaseThreadFilter();
                }
                else
                {
                    // some errors occured while parsing
                    LogEntries.Clear();

                    filteredLogEntries = new List<LogEntryViewModel>();

                    RaisePropertyChanged(() => LogEntries);

                    RaisePropertyChanged(() => FilteredLogEntries);

                    RaisePropertyChanged(() => EntriesCount);

                    switch (result)
                    {
                        case PROCESS_RESULT.DATA_PARSE_ERROR:
                            RaiseEventInvoker(EventDateFormatException);
                            break;
                        case PROCESS_RESULT.LEVEL_PARSE_ERROR:
                            RaiseEventInvoker(EventLevelParseException);
                            break;
                        case PROCESS_RESULT.UNAUTHORIZED_ACCESS_ERROR:
                            RaiseEventInvoker(UnauthorizedException);
                            break;
                        case PROCESS_RESULT.GENERIC_ERROR:
                            RaiseEventInvoker(EventGenericException);
                            break;
                    }
                }


            }, tokenSource.Token
            );

        }

        private void FilterEntries()
        {
            filteredLogEntries = LogEntries.ToList();

            filteredLogEntries = FilterLogEntriesByFile(filteredLogEntries).ToList();

            filteredLogEntries = FilterLogEntriesByLevel(filteredLogEntries).ToList();

            filteredLogEntries = FilterLogEntriesByDate(filteredLogEntries).ToList();

            filteredLogEntries = FilterLogEntriesByText(filteredLogEntries).ToList();
        }

        private void AsyncFilterEntries()
        {
            #region no more used

            //Observable.Start(() =>
            //    {
            //        Status = "Applying filter..";

            //        FilterEntries();
            //    }
            //).Subscribe(_ =>
            //    {
            //        RaisePropertyChanged(() => FilteredLogEntries);

            //        Status = "Done";
            //    }
            //);

            #endregion

            Task.Factory.StartNew(() =>
            {
                while (true)
                {
                    mre.WaitOne();

                    Status = "Applying filter..";

                    // this delay allow the UI to complete some updates (like auto check/uncheck list) before executing                    
                    Thread.Sleep(FILTER_EXECUTING_DELAY);

                    FilterEntries();

                    // no more request, refresh content
                    RaisePropertyChanged(() => FilteredLogEntries);

                    Status = "Done";

                    mre.Reset();

                }
            }, TaskCreationOptions.LongRunning);
        }

        void FilterLogEntriesExecute()
        {
            ReleaseThreadFilter();
        }

        private void ReleaseThreadFilter()
        {
            //isRefreshRequested = true;

            mre.Set();
        }


        private void ExclusiveSelectItem(ISelectable selectable, IList<ISelectable> list)
        {
            int levelSelectedCount = list.Where(x => x.IsSelected == true).Count();
            int levelCount = list.Count;
            bool isLevelSelected = selectable.IsSelected;

            bool result = (isLevelSelected && levelSelectedCount == 1);

            list.All(x => { x.IsSelected = result; return true; });

            if (result == false)
                selectable.IsSelected = true;
        }

        private IEnumerable<LogEntryViewModel> FilterLogEntriesByText(IEnumerable<LogEntryViewModel> entries)
        {
            IEnumerable<LogEntryViewModel> output = entries;

            if (String.IsNullOrEmpty(textFilter) == false)
            {
                string pattern = matchWholeWord ?
                String.Format(@"\b{0}\b", Regex.Escape(textFilter)) :
                Regex.Escape(textFilter);

                RegexOptions options = RegexOptions.None;

                if (matchIgnoreCase)
                    options |= RegexOptions.IgnoreCase;

                output = from entry in entries
                         where Regex.Match(entry.Message, pattern, options).Success
                         select entry;

            }

            return output;
        }

        private IEnumerable<LogEntryViewModel> FilterLogEntriesByDate(IEnumerable<LogEntryViewModel> entries)
        {
            IEnumerable<LogEntryViewModel> output = entries;

            if (startDate.Date != DateTime.MinValue.Date)
            {
                output = output.Where(entry => entry.DTime >= startDate);
            }
            if (endDate.Date != DateTime.MinValue.Date)
            {
                output = output.Where(entry => entry.DTime <= endDate);
            }

            return output;
        }

        private IEnumerable<LogEntryViewModel> FilterLogEntriesByLevel(IEnumerable<LogEntryViewModel> entries)
        {
            IEnumerable<LogEntryViewModel> output = entries;

            output = from entry in entries
                     where listLevel.Where(x => x.IsSelected == true)
                        .Select(x => x.Level).Contains(entry.Level)
                     select entry;

            return output;
        }

        private IEnumerable<LogEntryViewModel> FilterLogEntriesByFile(IEnumerable<LogEntryViewModel> entries)
        {
            IEnumerable<LogEntryViewModel> output = entries;

            output = from entry in entries
                     where listFiles.Where(x => x.IsSelected == true)
                        .Select(x => listFiles.IndexOf(x)).Contains(entry.FileIndex)
                     select entry;

            return output;
        }

        void RefreshLastDateListExecute()
        {
            if (dateList != null)
                EndDateList = dateList.Where(o => o.CompareTo(startDate.Date) >= 0).ToList();
        }

        void ExlusiveSelectLevelExecute(ISelectable selectable)
        {
            ExclusiveSelectItem(selectable, listLevel.Cast<ISelectable>().ToList());
        }

        void ExlusiveSelectFileExecute(ISelectable selectable)
        {
            ExclusiveSelectItem(selectable, listFiles.Cast<ISelectable>().ToList());
        }

        void CancelProcessFileExecute()
        {
            tokenSource.Cancel();
        }

        void OpenDemoFileExecute()
        {
            string path = Path.GetDirectoryName(Utility.GetAppLocation());

            path = Path.Combine(path, "Demo");

            string configFile = Path.Combine(path, "demo.config");

            if (File.Exists(configFile))
            {
                OptionsVM.ImportSettingsFromFile(configFile);
            }

            string testlog1 = Path.Combine(path, "test_part 1.log");
            string testlog2 = Path.Combine(path, "test_part 2.log");

            string[] files = new string[] { testlog1, testlog2 };

            foreach (string file in files)
            {
                if (File.Exists(configFile) == false)
                {
                    return;
                }
            }

            listFiles = new List<FileViewModel>();

            files.All<string>(obj =>
            {
                listFiles.Add(new FileViewModel { Filename = obj, IsSelected = true });
                return true;
            });

            RaisePropertyChanged(() => ListFiles);

            AsyncProcessFiles(files);

            RaiseEventInvoker(HomeViewRequestedEvent);
        }

        void DragEnterExecute(DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop, false) == true)
            {
                string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);

                if (files.Length > 0)
                {
                    e.Effects = DragDropEffects.All;
                    RaiseEventInvoker(HomeViewRequestedEvent);
                }
            }
        }

        void DropExecute(DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop, false) == true)
            {
                string[] data = (string[])e.Data.GetData(DataFormats.FileDrop);

                IList<string> list = new List<string>();

                foreach (string path in data)
                {
                    bool isDir = (File.GetAttributes(path) & FileAttributes.Directory) == FileAttributes.Directory;

                    if (isDir)
                    {
                        string[] filesInPath = Directory.GetFiles(path, "*", SearchOption.AllDirectories);

                        ((List<string>)list).AddRange(filesInPath);
                    }
                    else
                        list.Add(path);
                }

                StartProcessFiles(list.ToArray<string>());
            }
        }

        void OpenWithExternalEditorExecute(LogEntryViewModel selectedEntry)
        {
            ExternalEditorViewModel extEditorVM = mainVM.ExternalEditorVM;

            string executable = extEditorVM.ExePath;
            string commandline = extEditorVM.CommandArgs;

            string commandArgs = commandline ?? String.Empty;

            if (String.IsNullOrEmpty(commandline) == false)
            {
                IDictionary<string, string> dictoReplace = new Dictionary<string, string>
                {
                    {"%f", String.Format("\"{0}\"", listFiles[selectedEntry.FileIndex].Filename)},                                
                    {"%d", selectedEntry.DTime.ToString("dd-MM-yyyy")},
                    {"%m", selectedEntry.Message},
                    {"%n", selectedEntry.Line.ToString()},
                    {"%l", selectedEntry.Level.ToString()},                                  
                };

                foreach (KeyValuePair<string, string> entry in dictoReplace)
                {
                    Regex rgx = new Regex(entry.Key, RegexOptions.IgnoreCase);

                    Match match = rgx.Match(commandline);

                    if (match.Success)
                        commandArgs = commandArgs.Replace(match.Value, entry.Value);
                }
            }

            ProcessStartInfo info = new ProcessStartInfo(executable);
            info.Arguments = commandArgs.ToString();

            Process.Start(info);
        }

        #endregion

        #region Commands

        public ICommand Open { get { return new MvvmFoundation.Wpf.RelayCommand(OpenExecute); } }
        public ICommand ReloadFile { get { return new MvvmFoundation.Wpf.RelayCommand(ReloadFileExecute); } }
        public ICommand RefreshLastDateList { get { return new MvvmFoundation.Wpf.RelayCommand(RefreshLastDateListExecute); } }
        public ICommand FilterLogEntries { get { return new MvvmFoundation.Wpf.RelayCommand(FilterLogEntriesExecute); } }
        public ICommand ExlusiveSelectLevel { get { return new MvvmFoundation.Wpf.RelayCommand<ISelectable>(ExlusiveSelectLevelExecute); } }
        public ICommand ExlusiveSelectFile { get { return new MvvmFoundation.Wpf.RelayCommand<ISelectable>(ExlusiveSelectFileExecute); } }
        public ICommand CancelProcessFile { get { return new MvvmFoundation.Wpf.RelayCommand(CancelProcessFileExecute); } }
        public ICommand OpenDemoFile { get { return new MvvmFoundation.Wpf.RelayCommand(OpenDemoFileExecute); } }
        public ICommand DragEnterCommand { get { return new MvvmFoundation.Wpf.RelayCommand<DragEventArgs>(param => DragEnterExecute(param)); } }
        public ICommand DropCommand { get { return new MvvmFoundation.Wpf.RelayCommand<DragEventArgs>(param => DropExecute(param)); } }
        public ICommand OpenWithExternalEditor { get { return new MvvmFoundation.Wpf.RelayCommand<LogEntryViewModel>(param => OpenWithExternalEditorExecute(param)); } }

        #endregion
    }
}
